类utools执行逻辑:获取当前执行命令目录+执行CLI逻辑
概述
本节解决 Electron 应用中 CLI 模块运行时的路径问题:process.cwd() 返回的是 Electron 应用自身的路径,而非用户期望初始化模板的目标路径。通过分析 uTools 的交互模式,实现获取用户当前工作目录并正确执行 CLI 初始化逻辑。
问题分析
process.cwd() 在 Electron 中的行为
| 运行环境 | process.cwd() 返回值 | 是否符合预期 |
|---|---|---|
终端 node cli.js | 当前终端工作目录 | 符合 |
| Electron 应用 | Electron 应用安装/运行路径 | 不符合 |
在 Electron 应用中,Node.js 内置模块的 process.cwd() 指向的是 Electron 应用本身的执行路径,而非用户希望初始化模板的目标目录。
uTools 的交互参考
uTools 等桌面效率工具的典型交互流程:
- 用户在输入框中输入关键字(如"压缩图片")
- 工具自动获取当前文件管理器所处的目录
- 基于该目录执行对应的操作
解决方案:两种思路
思路一:用户界面输入
提供一个输入框,让用户手动指定目标路径:
<template>
<div class="cli-panel">
<el-input
v-model="targetPath"
placeholder="请输入项目初始化路径"
clearable
/>
<el-button @click="runCli">执行初始化</el-button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const targetPath = ref('')
async function runCli() {
// 通过 IPC 将用户指定的路径传递给主进程
await window.electronAPI.runCli({
cwd: targetPath.value
})
}
</script>
vue
思路二:自动捕获当前目录(类 uTools)
通过系统 API 获取用户当前操作的文件路径:
// main process - 获取当前活动窗口的路径
import { app, BrowserWindow } from 'electron'
import path from 'path'
// macOS: 通过 AppleScript 获取 Finder 当前目录
function getCurrentDirectory(): string {
const platform = process.platform
if (platform === 'darwin') {
// macOS: 使用 AppleScript 获取 Finder 最前端窗口路径
const { execSync } = require('child_process')
try {
const dir = execSync(
'osascript -e \'tell application "Finder" to get POSIX path of (insertion location as alias)\''
).toString().trim()
return dir
} catch {
return app.getPath('home')
}
}
if (platform === 'win32') {
// Windows: 获取资源管理器当前路径
const { execSync } = require('child_process')
try {
const result = execSync(
'powershell -command "(New-Object -ComObject Shell.Application).Windows() | ForEach-Object { $_.Document.Folder.Self.Path }" | Select-Object -First 1'
).toString().trim()
return result
} catch {
return app.getPath('home')
}
}
return app.getPath('home')
}
typescript
CLI 执行逻辑重构
修复前的 CLI 代码
// cli.ts - 问题代码
import { exec } from 'child_process'
function createProject(template: string) {
// 问题:cwd 指向 Electron 应用路径
const cwd = process.cwd() // ❌ 错误路径
exec(`node create-template.js --template ${template}`, {
cwd // 会在错误路径下执行
})
}
typescript
修复后的 CLI 代码
// cli.ts - 修复后的代码
import { exec } from 'child_process'
import path from 'path'
interface CliOptions {
cwd: string // 用户指定的目标路径
template: string // 模板名称
projectName: string
}
function createProject(options: CliOptions) {
const { cwd, template, projectName } = options
const targetPath = path.resolve(cwd, projectName)
// 使用用户指定的路径作为工作目录
exec(`node ${getScriptPath()} --template ${template}`, {
cwd: targetPath // ✅ 正确的目标路径
}, (error, stdout, stderr) => {
if (error) {
console.error(`执行错误: ${error.message}`)
return
}
console.log(`项目 ${projectName} 创建成功`)
})
}
// 获取 CLI 脚本的绝对路径(与 cwd 无关)
function getScriptPath(): string {
// __dirname 始终指向脚本所在位置
return path.resolve(__dirname, 'create-template.js')
}
typescript
IPC 通信桥接
// preload.ts
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('electronAPI', {
runCli: (options: { cwd: string; template: string }) =>
ipcRenderer.invoke('cli:run', options),
getCurrentDir: () => ipcRenderer.invoke('cli:getCurrentDir')
})
// main process
ipcMain.handle('cli:run', async (_event, options) => {
const { cwd, template } = options
// 使用正确的 cwd 执行 CLI
return createProject({
cwd,
template,
projectName: path.basename(cwd) || 'my-project'
})
})
ipcMain.handle('cli:getCurrentDir', () => {
return getCurrentDirectory()
})
typescript
跨平台兼容处理
| 平台 | 获取当前目录方式 | 注意事项 |
|---|---|---|
| macOS | AppleScript + Finder API | 需要处理 Finder 未打开窗口的情况 |
| Windows | PowerShell + Shell.Application | 编码问题,路径使用反斜杠 |
| Linux | 读取 /proc 或 D-Bus | 不同桌面环境实现不同 |
// 统一路径处理
function normalizePath(p: string): string {
return path.normalize(p.replace(/\\/g, '/'))
}
typescript
小结
- Electron 中
process.cwd()返回应用自身路径,不适合作为 CLI 的目标目录 - 参考 uTools 模式,通过系统 API 或用户输入获取正确的工作目录
- 使用 IPC 通信将渲染进程的目标路径传递给主进程执行
- 注意跨平台路径差异,做好 normalize 处理
↑